﻿using MyoSharp.Communication;
using MyoSharp.Device;
using MyoSharp.Exceptions;
using MyoSharp.HackNighter.Classes;
using MyoSharp.HackNighter.Controls;
using MyoSharp.Poses;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;

namespace MyoSharp.HackNighter
{
    public partial class MainWindow : Window
    {
        #region Members

        public int Points { get; set; }
        private Random Random = new Random();

        private Animation Player { get; set; }
        private List<Animation> Enemies { get; set; }

        #endregion

        #region Constructor

        public MainWindow()
        {
            InitializeComponent();
            Loaded += MainWindow_Loaded;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            InitializeGame();
            InitializeMyo();

            StartGame();
        }

        private void InitializeGame()
        {
            var playerUc = new PlayerUC();
            this.Player = new Animation(playerUc) { PosX = 0 };
            this.Canvas.Children.Add(playerUc);
            this.Enemies = new List<Animation>();
        }

        private void InitializeMyo()
        {
            var channel = Channel.Create(ChannelDriver.Create(ChannelBridge.Create(), MyoErrorHandlerDriver.Create(MyoErrorHandlerBridge.Create())));

            var hub = Hub.Create(channel);
            hub.MyoConnected += (sender, e) =>
            {
                e.Myo.Vibrate(VibrationType.Short);
                e.Myo.Unlock(UnlockType.Hold);

                var heldPose = HeldPose.Create(e.Myo, Pose.WaveIn, Pose.WaveOut);
                heldPose.Interval = TimeSpan.FromSeconds(0.5);
                heldPose.Start();
                heldPose.Triggered += Pose_Triggered;

                e.Myo.PoseChanged += Myo_PoseChanged;
                e.Myo.Locked += Myo_Locked;
                e.Myo.Unlocked += Myo_Unlocked;
            };

            hub.MyoDisconnected += (sender, e) =>
            {
                e.Myo.PoseChanged -= Myo_PoseChanged;
                e.Myo.Locked -= Myo_Locked;
                e.Myo.Unlocked -= Myo_Unlocked;
            };

            channel.StartListening();
        }

        #endregion Constructor

        #region Events

        private void Pose_Triggered(object sender, PoseEventArgs e)
        {
            this.Dispatcher.BeginInvoke(new Action(() =>
            {
                switch (e.Myo.Pose)
                {
                    case Pose.WaveIn:
                        this.Player.VelX = -7;
                        break;
                    case Pose.WaveOut:
                        this.Player.VelX = 7;
                        break;
                }
            }));
        }

        private void Myo_PoseChanged(object sender, PoseEventArgs e)
        {
            this.Dispatcher.BeginInvoke(new Action(() =>
            {
                switch (e.Myo.Pose)
                {
                    case Pose.Rest:
                        break;
                    case Pose.Fist:

                        break;
                    case Pose.FingersSpread:
                        this.Player.VelY = 12;
                        break;
                    case Pose.DoubleTap:
                        if (!this.Player.Active)
                        {
                            var playerUc = new PlayerUC();
                            this.Player = new Animation(playerUc) { PosX = 0 };
                            this.Canvas.Children.Add(playerUc);
                        }
                        break;
                    case Pose.Unknown:
                        break;
                    default:
                        break;
                }
            }));
        }

        private void Myo_Unlocked(object sender, MyoEventArgs e)
        {
            //Console.WriteLine("{0} arm Myo has unlocked!", e.Myo.Arm);
        }

        private void Myo_Locked(object sender, MyoEventArgs e)
        {
            //Console.WriteLine("{0} arm Myo has locked!", e.Myo.Arm);
        }

        #endregion Events

        #region Methods

        private async void StartGame()
        {
            while (true)
            {
                await Task.Delay(33);

                var activeEnemiesCount = this.Enemies.Count(x => x.Active);

                if (activeEnemiesCount == 0)
                    CreateEnemy();
                else if (activeEnemiesCount <= 1)
                {
                    if (this.Random.NextDouble() < 0.02)
                        CreateEnemy();
                }

                this.Player.Move();
                foreach (var enemy in this.Enemies)
                    enemy.Move();

                ValidateCollision();
                ValidatePosition();
            }
        }

        private void ValidatePosition()
        {
            var width = this.Canvas.ActualWidth;

            foreach (var enemy in this.Enemies)
            {
                if (enemy.PosX <= -100)
                    enemy.PosX = width;
            }
        }

        private void ValidateCollision()
        {
            foreach (var enemy in this.Enemies.Where(x => x.Active))
            {
                if (enemy.PosX - this.Player.PosX <= 30 && enemy.PosX - this.Player.PosX >= -30)
                {
                    if (this.Player.VelY == 0)
                    {
                        if (enemy.Active && this.Player.Active)
                        {
                            this.Player.Active = false;
                            ((PlayerUC)this.Player.GameElement).OnDie += (s, a) =>
                            {
                                this.Canvas.Children.Remove(this.Player.GameElement);
                            };
                            ((PlayerUC)this.Player.GameElement).Die();
                            this.Points = 0;
                            this.txtPoints.Text = this.Points.ToString();
                        }
                    }
                    else
                    {
                        if (this.Player.Active && enemy.Active)
                        {
                            enemy.Active = false;
                            ((EnemyUC)enemy.GameElement).Die();
                            this.Points = this.Points + 100;
                            this.txtPoints.Text = this.Points.ToString();
                        }
                    }
                }
            }
        }

        private void CreateEnemy()
        {
            var width = this.Canvas.ActualWidth;
            var velX = -this.Random.Next(5, 11);

            var enemyElement = new EnemyUC();
            enemyElement.OnDie += (s, a) => { this.Canvas.Children.Remove(enemyElement); };
            var animation = new Animation(enemyElement) { PosX = width, VelX = velX };
            this.Enemies.Add(animation);
            this.Canvas.Children.Add(enemyElement);
        }

        #endregion Methods
    }
}